package com.zengqingfa.rocketmq.util;

import org.springframework.aop.framework.AopProxyUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;

public class ClassUtil {

    /**
     * 获取泛型接口的实际参数
     *
     * @param object                需要判断泛型实际类型的对象
     * @param genericType           需要被判断的泛型接口
     * @param genericParameterIndex 需要被判断的泛型接口的泛型参数下标, 0表示第1个
     * @return Type      泛型参数的实际类型
     */
    public static Type getGenericInterfacesParameterType(Object object, Class genericType,
            Integer genericParameterIndex) {
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(object);
        Type matchedGenericInterface = null;
        while (Objects.nonNull(targetClass)) {
            Type[] interfaces = targetClass.getGenericInterfaces();
            if (Objects.nonNull(interfaces)) {
                for (Type type : interfaces) {
                    if (type instanceof ParameterizedType && Objects
                            .equals(((ParameterizedType) type).getRawType(), genericType)) {
                        matchedGenericInterface = type;
                        break;
                    }
                }
            }
            targetClass = targetClass.getSuperclass();
        }
        return getType(genericParameterIndex, matchedGenericInterface);
    }

    /**
     * 获取泛型类的实际参数
     *
     * @param object                需要判断泛型实际类型的对象
     * @param genericType           需要被判断的泛型类
     * @param genericParameterIndex 需要被判断的泛型类的泛型参数下标, 0表示第1个
     * @return Type      泛型参数的实际类型
     */
    public static Type getGenericClassParameterType(Object object, Class genericType, Integer genericParameterIndex) {
        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(object);

        Type matchedGenericClass = null;
        while (Objects.nonNull(targetClass)) {
            Type superclass = targetClass.getGenericSuperclass();
            if (Objects.nonNull(superclass)) {
                if (superclass instanceof ParameterizedType && Objects
                        .equals(((ParameterizedType) superclass).getRawType(), genericType)) {
                    matchedGenericClass = superclass;
                    break;
                }
            }
            targetClass = targetClass.getSuperclass();
        }
        return getType(genericParameterIndex, matchedGenericClass);
    }

    private static Type getType(Integer genericParameterIndex, Type matchedGenericClass) {
        if (Objects.isNull(matchedGenericClass)) {
            return Object.class;
        }

        Type[] actualTypeArguments = ((ParameterizedType) matchedGenericClass).getActualTypeArguments();
        if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {
            return actualTypeArguments[genericParameterIndex];
        }
        return Object.class;
    }
}
